#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <EGL/egl.h>
#include <EGL/eglplatform.h>
#include <GLES3/gl3.h>

#include <android/log.h>
#include <media/NdkImageReader.h>

#define FRAME_RATE 4
//#define USE_CPU_DRAW

static void ImageReader_ImageCallback(void* context, AImageReader* reader)
{
    int socketFd = (int)(size_t)context;
    AImage* image = NULL;
    AHardwareBuffer* hardwareBuffer = NULL;
    AImageReader_acquireNextImage(reader, &image);
    if (!image) return;
    AImage_getHardwareBuffer(image, &hardwareBuffer);
    AHardwareBuffer_sendHandleToUnixSocket(hardwareBuffer, socketFd);
    AImage_delete(image);
}

int main(int argc, char* argv[])
{
    const char* socket_name = NULL;
    if (argc > 1) socket_name = argv[1];
    if (!socket_name)
    {
        __android_log_print(ANDROID_LOG_ERROR, NULL, "create socket failed");
        return -2;
    }
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = {.sun_family = AF_UNIX};
    strcpy(addr.sun_path + 1, socket_name);
    addr.sun_path[0] = 0;
    if (connect(fd, (struct sockaddr *) &addr,
                addr.sun_path + strlen(socket_name) + 1 - (char *) &addr) < 0) {
        __android_log_print(ANDROID_LOG_ERROR, NULL, "connect socket failed");
        return -1;
    }
    __android_log_print(ANDROID_LOG_INFO, NULL, "connect socket %d succeeded", fd);
    const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
                           AHARDWAREBUFFER_USAGE_CPU_READ_RARELY |
                           AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
                           AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
    AImageReader *imageReader = NULL;
    ANativeWindow *window = NULL;
    AImageReader_newWithUsage(512, 512, AIMAGE_FORMAT_RGBA_8888, usage, 1, &imageReader);
    struct AImageReader_ImageListener imageListener = {(void *) (size_t) fd,
                                                       &ImageReader_ImageCallback};
    AImageReader_setImageListener(imageReader, &imageListener);
    AImageReader_getWindow(imageReader, &window);


    uint32_t index = 0, colors[] = { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFFFF };
#ifdef USE_CPU_DRAW
    for (;;)
    {
        uint32_t color = colors[index = (index + 1) % (sizeof(colors) / sizeof(*colors))];
        struct ANativeWindow_Buffer buffer = { 0 };
        ANativeWindow_lock(window, &buffer, NULL);
        for (int i = 0; i < buffer.stride * buffer.height; ++i)
        {
            ((uint32_t*)buffer.bits)[i] = color;
        }
        ANativeWindow_unlockAndPost(window);
        usleep(1000000 / FRAME_RATE);
    }
#else
    EGLint major = 1, minor = 5;
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(display, &major, &minor);

    EGLint attribute[] =
            {
                    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                    EGL_BUFFER_SIZE, 32,
                    EGL_ALPHA_SIZE, 8,
                    EGL_BLUE_SIZE, 8,
                    EGL_GREEN_SIZE, 8,
                    EGL_RED_SIZE, 8,
                    EGL_NONE
            };
    GLint numConfig = 1;
    EGLConfig config = NULL;
    eglChooseConfig(display, attribute, &config, numConfig, &numConfig);
    EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
    EGLSurface surface = eglCreateWindowSurface(display, config, window, NULL);
    eglMakeCurrent(display, surface, surface, context);

    for (;;)
    {
        uint32_t color = colors[index = (index + 1) % (sizeof(colors) / sizeof(*colors))];
        glClearColor((color >> 16 & 0xFF) / 255.0, (color >> 8 & 0xFF) / 255.0, (color & 0xFF) / 255.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        eglSwapBuffers(display, surface);
        usleep(1000000 / FRAME_RATE);
    }
#endif
    AImageReader_delete(imageReader);
    return 0;
}